#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>

int start_server()
{
	struct addrinfo hints, *result = NULL, *rp = NULL;
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_flags = AI_PASSIVE;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = 0;

	if (getaddrinfo(NULL, "8000", &hints, &result) < 0) {
		perror("getaddrinfo");
		_exit(EXIT_FAILURE);
	}

	for (rp = result; rp; rp = rp->ai_next) {
		int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
		if (fd < 0) {
			perror("socket");
			continue;
		}
		int on = 1;
		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
			perror("setsockopt");
			close(fd);
			continue;
		}
		if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0) {
			perror("bind");
			close(fd);
			continue;
		}
		listen(fd, 10);
		freeaddrinfo(result);
		return fd;
	}

	freeaddrinfo(result);
	return -1;
}

void ioact(int sig, siginfo_t *info, void *context)
{
	if (sig != SIGRTMIN + 1) {
		return;
	}
	if (info->si_code == POLL_IN) {
		char buf[1024];
		if (info->si_band | POLLIN) {
			int len = read(info->si_fd, buf, sizeof(buf));
			if (len > 0) {
				buf[len] = 0;
			} else {
				close(info->si_fd);
				return;
			}
			printf("recv %s", buf);
		}
		if (info->si_band | POLLOUT) {
			printf("can write\n");
		}
		if (info->si_band | POLLIN) {
			printf("can read\n");
		}
	}

	if (info->si_code == POLL_OUT) {
		printf("recv POLL_OUT 2\n");
	}
}

int main()
{
	int limit = sysconf(_SC_SIGQUEUE_MAX);
	struct rlimit rt;
	getrlimit(RLIMIT_SIGPENDING, &rt);
	printf("QUEUE MAC = %d %lu %lu\n", limit, rt.rlim_cur, rt.rlim_max);
	int fd = start_server();
	if (fd < 0) {
		perror("start_server");
		_exit(EXIT_FAILURE);
	}

	struct sigaction sa;
	bzero(&sa, sizeof(sa));
	sa.sa_flags = SA_RESTART | SA_SIGINFO;
	sigemptyset(&sa.sa_mask);
	sa.sa_sigaction = ioact;
	if (sigaction(SIGRTMIN + 1, &sa, NULL) < 0) {
		perror("sigaction");
		_exit(EXIT_FAILURE);
	}

	for (;;) {
		int cfd = accept(fd, NULL, NULL);
		if (fcntl(cfd, F_SETSIG, SIGRTMIN + 1) < 0) {
			perror("fcntl 3");
			_exit(EXIT_FAILURE);
		}
		if (fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL) | O_NONBLOCK | O_ASYNC) < 0) {
			perror("fcntl");
			_exit(EXIT_FAILURE);
		}
		if (fcntl(cfd, F_SETOWN, getpid()) < 0) {
			perror("fcntl");
			_exit(EXIT_FAILURE);
		}
	}
}